#ifndef GRITC_SERVER_EFFECT_HPP
#define GRITC_SERVER_EFFECT_HPP

namespace gritc { namespace server {

	template<class Traits> struct effect
	{
		typedef Traits traits_type;
		typedef typename Traits::actor_type actor_type;
		typedef typename Traits::string_type string_type;
		typedef typename Traits::world_type world_type;
		typedef unsigned long ulong;

		union
		{
			struct
			{
				ulong out_bit		: 1;
				ulong cooldown_bit	: 1;
			};
			ulong flag_bits;
		};

		string_type const& name_;
		world_type & world_;
		actor_type & source_;
		actor_type & target_;

		effect(string_type const& n, world_type & w, actor_type & s, actor_type & t)
			:	flag_bits(0)
			,	out_bit(1)
			,	name_(n)
			,	world_(w)
			,	source_(s)
			,	target_(t) {}

		world_type & world() { return world_; }
		actor_type & source() { return source_; }
		actor_type & target() { return target_; }
		string_type const& name() { return name_; }
	};

	template<class Traits> struct damage : effect<Traits>
	{
		ulong ammount;

		damage(string_type const& n, world_type & w, actor_type & s, actor_type & t, ulong a)
			:	effect(n,w,s,t)
			,	ammount(a) {}

		void apply()
		{
			if(out_bit == true)
				out_bit = false;
			else
			{
				target_.soak += ammount;

				target_.room().response() 
					<< 	" " 
					<<	name() 
					<<	" " 
					<<	source().name() 
					<<	" " 
					<<	target().name() 
					<<	" " 
					<<	ammount;
			}
		}
	};

	template<class Traits> struct heal : effect<Traits>
	{
		ulong ammount;

		heal(string_type const& n, world_type & w, actor_type & s, actor_type & t, ulong a)
			:	effect(n,w,s,t)
			,	ammount(a) {}

		void apply()
		{
			if(out_bit == true)
				out_bit = false;
			else
				target_.soak -= ammount;
		}
	};

	template<class Traits> struct buffor : effect<Traits>
	{
		struct applier_base
		{
			virtual ~applier_base() {}
			virtual void apply(buffor<Traits> &) = 0;
		};
		template<class F> struct applier : applier_base
		{
			F & function_;
			applier(F & f) : function_(f) {}
			virtual ~applier() {}
			virtual void apply(buffor<Traits> & b)
			{
				function_(b);
			}
		};

		applier_base & applier_;

		buffor(string_type const& n, world_type & w, actor_type & s, actor_type & t, applier_base & a)
			:	effect(n,w,s,t)
			,	applier_(a) {}

		void apply() 
		{ 
			if(out_bit) 
				out_bit = false;
			else
				applier_.apply(*this); 
		}
	};

	template<class T> struct tick
	{
		typedef T traits_type;
		typedef typename T::world_type world_type;
		typedef typename T::time_type time_type;

		world_type & world_;
		world_type & world() { return world_; }

		const time_type before_;
		const time_type now_;
		const time_type elapsed_;

		time_type before() const { return before_; }
		time_type now() const { return now_; }
		time_type elapsed() const { return elapsed_; }

		tick(world_type & w, const time_type b, const time_type n) 
			:	world_(w)
			,	before_(b)
			,	now_(n)
			,	elapsed_(n - b) {}

		void apply() {}
	};

	template<class T> struct null_effect
	{
		typedef typename T traits_type;
		void apply() {}
	};

	struct new_effect_
	{
		template<class Effect>
		typename Effect::traits_type::effect_visitor::clientv<Effect> * operator ()(Effect & effect_) const
		{
			return new Effect::traits_type::effect_visitor::clientv<Effect>(effect_);
		}
	} const new_effect;

	struct apply_effect_
	{
		struct applier
		{
			template<class T> void operator ()(T & t) const { t.apply(); }
		};
		struct unset_outbit
		{
			template<class T> void operator ()(tick<T> & t) const { }
			template<class T> void operator ()(effect<T> & t) const { t.out_bit = false; }
			template<class T> void operator ()(null_effect<T> &) const {}
		};
		template<class Effect> void operator ()(Effect & effect_) const
		{
			Effect::traits_type::effect_pointer p 
				(	new Effect::traits_type::effect_visitor::clientr<Effect>(effect_));

			effect_.out_bit = true;
			effect_.source().act(p);
			(*p)(applier());

			(*p)(unset_outbit());
			effect_.target().act(p);
			(*p)(applier());
		}
	} const apply_effect;

	struct apply_buff_
	{
		template<class String, class World, class Actor, class Applier>
		void operator () (String & name_, World & world_, Actor & source_, Actor & target_, Applier & applier_) const
		{
			apply_effect ( buffor<Actor::traits_type>
				(	name_
				,	world_
				,	source_
				,	target_
				,	buffor<Actor::traits_type>::applier<Applier>(applier_)));
		}
	} const apply_buff;

	struct apply_damage_
	{
		template<class String, class World, class Actor, class Damage>
		void operator () (String & name_, World & world_, Actor & source_, Actor & target_, Damage & damage_) const
		{
			apply_effect ( damage<Actor::traits_type>
				(	name_
				,	world_
				,	source_
				,	target_
				,	damage_ ));
		}
	} const apply_damage;

	struct apply_heal_
	{
		template<class String, class World, class Actor, class Heal>
		void operator () (String & name_, World & world_, Actor & source_, Actor & target_, Heal & heal_) const
		{
			apply_effect ( heal<Actor::traits_type>
				(	name_
				,	world_
				,	source_
				,	target_
				,	heal_ ));
		}
	} const apply_heal;
}}

#endif